package com.ml.room.terrace.component;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.ml.room.common.utils.IPUtils;
import com.ml.room.repository.entity.SysExceptionLog;
import com.ml.room.repository.entity.SysLog;
import com.ml.room.service.ISysExceptionLogService;
import com.ml.room.service.ISysLogService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: AutoLogAspect
 * @Decription: 此处填写类文件说明
 * @Author: xjs
 * @Date: 2021-05-20 13:41 星期四
 **/
@Aspect
@Component
@Order(2)
public class AutoLogAspect {

    @Autowired
    private ISysLogService logService;

    @Autowired
    private ISysExceptionLogService exceptionService;

    @Pointcut("@annotation(com.ml.room.terrace.component.AutoLog)")
    public void logPointCut() {
    }

    @Pointcut("execution(* com.ml.room.*.controller..*.*(..))")
    public void exceptionLogPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;

        //保存日志
        saveSysLog(point, time);

        return result;
    }

    @AfterThrowing(pointcut = "exceptionLogPointCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        SysExceptionLog excepLog = new SysExceptionLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName + "()";
            excepLog.setMethod(methodName);
            excepLog.setRequestParam(getRequestParams(request, joinPoint));
            excepLog.setIp(IPUtils.getIpAddr(request));
            //获取登录用户信息
            //LrAdmin admin = BaseController.getLoginUser();
            excepLog.setUsername("访客");
            excepLog.setNickName("访客");
            excepLog.setUri(request.getRequestURI());
            excepLog.setCreateTime(new Date());
            excepLog.setException(e.getClass().getName());
            exceptionService.save(excepLog);

        } catch (Exception e2) {
            e2.printStackTrace();
        }

    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLog dto = new SysLog();
        AutoLog syslog = method.getAnnotation(AutoLog.class);
        if (syslog != null) {
            String content = syslog.value();
            String logType = syslog.logType();
            String operateType = syslog.operateType();
            dto.setLogContent(content);
            dto.setLogType(logType);
            dto.setOperateType(operateType);
        }

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        dto.setMethod(className + "." + methodName + "()");

        //获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //请求的参数
        dto.setRequestParam(getRequestParams(request, joinPoint));
        //设置IP地址
        dto.setIp(IPUtils.getIpAddr(request));
        //设置请求路由
        dto.setUri(request.getRequestURI());

        //获取登录用户信息
        dto.setUsername("访客");
        dto.setNickName("访客");
        //耗时
        dto.setCostTime(time);
        dto.setCreateTime(new Date());
        //保存系统日志
        logService.save(dto);
    }

    /**
     * @param request:   request
     * @param joinPoint: joinPoint
     * @Description: 获取请求参数
     * @author: scott
     * @date: 2020/4/16 0:10
     * @Return: java.lang.String
     */
    private String getRequestParams(HttpServletRequest request, JoinPoint joinPoint) {
        String httpMethod = request.getMethod();
        String params = "";
        if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
            Object[] paramsArray = joinPoint.getArgs();
            // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
            //  https://my.oschina.net/mengzhang6/blog/2395893
            List<Object> arguments = new ArrayList();
            //Object[] arguments  = new Object[paramsArray.length];
            for (int i = 0; i < paramsArray.length; i++) {
                if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
                    //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                    //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                    continue;
                }
                arguments.add(paramsArray[i]);
                //arguments[i] = paramsArray[i];
            }
            //update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
            PropertyFilter profilter = new PropertyFilter() {
                @Override
                public boolean apply(Object o, String name, Object value) {
                    if (value != null && value.toString().length() > 500) {
                        return false;
                    }
                    return true;
                }
            };
            params = JSONObject.toJSONString(arguments, profilter);
            params = params.substring(1, params.length() - 1);
            //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
        } else {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                for (int i = 0; i < args.length; i++) {
                    params += paramNames[i] + ":" + args[i] + ",";
                }
                if (StringUtils.isNotEmpty(params)) {
                    params = params.substring(0, params.length() - 1);
                }
            }
        }

        byte bytes[] = params.getBytes();
        if (bytes.length>2550){
            params = "参数数据过长";
        }

        return params;
    }
}
